package cn.monkey.transport.netty.codec;

import io.netty.channel.*;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.TypeParameterMatcher;

public abstract class SimpleChannelOutboundHandler<T> extends ChannelOutboundHandlerAdapter {

    private final TypeParameterMatcher matcher;
    private final boolean autoRelease;

    public SimpleChannelOutboundHandler() {
        this(true);
    }

    public SimpleChannelOutboundHandler(boolean autoRelease) {
        matcher = TypeParameterMatcher.find(this, SimpleChannelOutboundHandler.class, "T");
        this.autoRelease = autoRelease;
    }

    public SimpleChannelOutboundHandler(Class<? extends T> inboundMessageType) {
        this(inboundMessageType, true);
    }

    public SimpleChannelOutboundHandler(Class<? extends T> inboundMessageType, boolean autoRelease) {
        matcher = TypeParameterMatcher.get(inboundMessageType);
        this.autoRelease = autoRelease;
    }

    protected boolean acceptInboundMessage(Object msg) {
        return matcher.match(msg);
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        boolean release = true;
        try {
            if (acceptInboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                T imsg = (T) msg;
                write0(ctx, imsg, promise);
            } else {
                release = false;
                ctx.write(msg, promise);
            }
        } finally {
            if (autoRelease && release) {
                ReferenceCountUtil.release(msg);
            }
        }
    }

    protected abstract void write0(ChannelHandlerContext ctx, T imsg, ChannelPromise promise) throws Exception;
}
